/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.validator.internal;

import java.util.*;
import java.util.Map.Entry;
import ch.simpleel.accessors.*;
import ch.trackedbean.validator.*;
import ch.trackedbean.validator.annotations.*;

/**
 * Information about a depth property.<br>
 * Used to represent a {@link DeepValidation} property.
 * 
 * @author M. Hautle
 */
public class DepthValidator {
    /** The property name. */
    protected final String name;

    /** The message for an invalid property. */
    protected final IErrorDescription message;

    /** The property accessor to use. */
    protected final IValueAccessor<?> read;

    /**
     * Default constructor.
     * 
     * @param name The property name
     * @param message The message for an invalid property
     * @param accessor The property accessor
     */
    public DepthValidator(String name, String message, IValueAccessor accessor) {
        this.name = name;
        this.message = new ErrorDescription(message);
        this.read = accessor;
    }

    /**
     * Validates this property on the given bean.
     * 
     * @param bean The bean
     * @param context The context
     * @return True if this property contains errors
     */
    public boolean validate(Object bean, IValidationContext context) {
        boolean err = false;
        final Object value = read.getValue(bean, null);
        // validate the content if it's non null and if it haven't been validated
        if (value == null || !context.stepIntoObject(name, value))
            return false;
        // step down the structure
        if (value instanceof Collection) {
            err |= validate((Collection) value, context);
        } else if (value instanceof Map) {
            err |= validate((Map) value, context);
        } else if (value.getClass().isArray()) {
            err |= validate((Object[]) value, context);
        } else {
            final IValidator val = ValidatorManager.getValidator(value.getClass());
            err |= val.validate(value, context);
        }
        context.stepOutOfObject(value, err ? message : null);
        return err;
    }

    /**
     * Validates the content of an array.
     * 
     * @param array The array
     * @param context The context
     * @return True if the validated array contains an error
     */
    protected boolean validate(Object[] array, IValidationContext context) {
        boolean err = false;
        for (int i = 0; i < array.length; i++) {
            if (array[i] == null || !context.stepIntoCollectionElement(i, array[i]))
                continue;
            final IValidator val = ValidatorManager.getValidator(array[i].getClass());
            err |= val.validate(array[i], context);
            context.stepOutOfCollectionElement();
        }
        return err;
    }

    /**
     * Validates the content of an list.
     * 
     * @param list The list
     * @param context The context
     * @return True if the validated list contains an error
     */
    protected boolean validate(Collection list, IValidationContext context) {
        boolean err = false;
        final Iterator it = list.iterator();
        for (int i = 0; it.hasNext(); i++) {
            final Object value = it.next();
            if (value == null || !context.stepIntoCollectionElement(i, value))
                continue;
            final IValidator val = ValidatorManager.getValidator(value.getClass());
            err |= val.validate(value, context);
            context.stepOutOfCollectionElement();
        }
        return err;
    }

    /**
     * Validates the content of an map.
     * 
     * @param map The map
     * @param context The context
     * @return True if the validated map contains an error
     */
    protected boolean validate(Map<?, ?> map, IValidationContext context) {
        boolean err = false;
        for (Entry e : map.entrySet()) {
            final Object value = e.getValue();
            if (value == null || !context.stepIntoMapElement(e.getKey(), value))
                continue;
            final IValidator val = ValidatorManager.getValidator(value.getClass());
            err |= val.validate(value, context);
            context.stepOutOfMapElement();
        }
        return err;
    }
}